package com.jjrwa.transform;

import com.intellij.ide.util.PackageChooserDialog;
import com.intellij.ide.util.TreeJavaClassChooserDialog;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.*;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.PsiUtil;
import com.jjrwa.component.ServiceTable;
import com.jjrwa.component.TableDataHandle;
import com.jjrwa.define.AutowiredParam;
import com.jjrwa.define.HandleControllerClass;
import com.jjrwa.define.HandleMethod;
import com.jjrwa.from.BuildClassTable;
import com.jjrwa.from.UIFieldTable;
import com.jjrwa.utils.CommonUtils;
import com.jjrwa.utils.FileHandleUtils;
import com.jjrwa.utils.FreemarkerUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public abstract class ServiceTransformHandler {

    public static void serviceToController(AnActionEvent event) {
        PsiJavaFile javaFile = FileHandleUtils.getPsiJavaFile(event);
        if (javaFile == null) {
            return;
        }

        //获取第一个class文件
        PsiClass serviceClass = javaFile.getClasses()[0];

        //是否配置@Service 标签
        PsiAnnotation entityAnn = serviceClass.getAnnotation("org.springframework.stereotype.Service");
        if (entityAnn == null) {
            throw new RuntimeException("仅支持Service类的转换");
        }
        HandleControllerClass handleClass = new HandleControllerClass();
        //元名
        String name = serviceClass.getName();
        if (name.endsWith("Service")) {
            name = name.replace("Service", "");
        }
        handleClass.setClassBeanName(name);
        //描述
        String docCommentText = FileHandleUtils.getDocCommentText(serviceClass.getDocComment());
        handleClass.setDescription(docCommentText);

        List<HandleMethod> handleMethods = new ArrayList<>();//方法
        Set<String> importContent = new HashSet<>();//导入
        importContent.add(serviceClass.getQualifiedName());


        for (PsiMethod method : serviceClass.getMethods()) {
            //只读取公共方法
            if (PsiUtil.ACCESS_LEVEL_PUBLIC != PsiUtil.getAccessLevel(method.getModifierList())) {
                continue;
            }
            HandleMethod handleMethod = psiMethodToHandleMethod(method, importContent);
            handleMethods.add(handleMethod);
        }

        handleClass.setMethods(handleMethods);
        handleClass.setImportContent(importContent);

        //表格通用处理
        IClassTable iClassTable = new IClassTable() {
            @Override
            public String getTitle() {
                return "Create Controller Class";
            }

            @Override
            public String getClassName() {
                return handleClass.getControllerName();
            }

            @Override
            public String getClassDescription() {
                return handleClass.getDescription();
            }
        };

        //成功处理
        IButtonEvent okButtonEvent = new IButtonEvent() {
            @Override
            public boolean handle(MouseEvent e, JFrame jFrame, JTextField classNameText, JTextField descriptionText) {
                jFrame.setVisible(false);
                handleClass.setControllerName(classNameText.getText());
                handleClass.setDescription(descriptionText.getText());
                PackageChooserDialog selector = new PackageChooserDialog(
                        "请选择一个存放Controller的包", event.getProject());
                selector.show();
                PsiPackage selectedPackage = selector.getSelectedPackage();
                if (selectedPackage == null) {
                    Messages.showErrorDialog(
                            String.format("未选择任何包"),
                            "Unselected");
                    jFrame.setVisible(true);
                    return false;
                }
                PsiDirectory directory = selectedPackage.getDirectories()[0];
                boolean b = FileHandleUtils.checkClassName(directory, handleClass.getControllerName());
                if (!b) {
                    jFrame.setVisible(true);
                    return false;
                }
                //创建java 文件
                FileHandleUtils.createJavaFile(event, "serviceToController.ftl",
                        handleClass, directory);
                return true;
            }
        };

        IButtonEvent fieldsButtonEvent = new IButtonEvent() {
            @Override
            public void handle(MouseEvent e) {
                new UIFieldTable(new TableDataHandle<>(
                        HandleMethod.class, handleClass.getMethods()),
                        new Dimension(800, 300), "方法详情",
                        h -> new ServiceTable(h));
            }

            @Override
            public String buttonName() {
                return "Methods";
            }
        };

        new BuildClassTable(iClassTable, okButtonEvent, fieldsButtonEvent);
    }

    private static HandleMethod psiMethodToHandleMethod(PsiMethod psiMethod, Set<String> importContent) {
        HandleMethod handleMethod = new HandleMethod();
        handleMethod.setName(psiMethod.getName());
        handleMethod.setDescription(FileHandleUtils.getDocCommentText(psiMethod.getDocComment()));

        PsiParameterList parameterList = psiMethod.getParameterList();
        if (parameterList.getParametersCount() > 1) {
            throw new RuntimeException(psiMethod.getName() + "方法中参数个数大于1个,请把参数封装成一个class类");
        } else if (parameterList.getParametersCount() == 0) {
            handleMethod.setParamType(null);
        } else {
            PsiType type = parameterList.getParameters()[0].getType();
//            PsiType type = parameterList.getParameter(0).getType();
            handleMethod.setParamType(type.getPresentableText());
            if (CommonUtils.needImport(type)) {
                importContent.add(type.getInternalCanonicalText());
            } else {
                handleMethod.setSingleParam(true);
                importContent.add("gynsh.scf.basics.define.param.SingleParam");
            }
        }

        PsiType returnType = psiMethod.getReturnType();
        if (returnType.getPresentableText().equals("void")) {
            handleMethod.setReturnType(null);
        } else {
            handleMethod.setReturnType(returnType.getPresentableText());
            if (CommonUtils.needImport(returnType)) {
                importContent.add(returnType.getInternalCanonicalText());
            }
        }
        handleMethod.setMappingValue(CommonUtils.buildMappingValue(handleMethod.getName()));

        return handleMethod;
    }

    public static void serviceMethodTransform(AnActionEvent event) {
        PsiElement psiElement = event.getData(PlatformDataKeys.PSI_ELEMENT);
        if (psiElement == null) {
            return;
        }
        //获取方法名称
        String methodName = psiElement.toString().replace("PsiMethod:", "");//获取到方法名

        PsiJavaFile serviceJavaFile = FileHandleUtils.getPsiJavaFile(event);
        PsiClass servicePsiClass = serviceJavaFile.getClasses()[0];
        PsiMethod[] methods = servicePsiClass.getMethods();
        PsiMethod psiMethod = null;
        for (PsiMethod method : methods) {
            if (method.getName().equals(methodName)) {
                psiMethod = method;
                break;
            }
        }
        if (psiMethod == null) {
            throw new RuntimeException("无法读取" + methodName + "方法");
        }

        //方法处理
        if (PsiUtil.ACCESS_LEVEL_PUBLIC != PsiUtil.getAccessLevel(psiMethod.getModifierList())) {
            throw new RuntimeException(methodName + "方法不是一个public方法");
        }
        Set<String> importContent = new HashSet<>();//导入

        HandleMethod handleMethod = psiMethodToHandleMethod(psiMethod, importContent);
        handleMethod.setServiceParam(CommonUtils.firstLowerCase(servicePsiClass.getName()));
        handleMethod.setServiceMethodName(psiMethod.getName());

        TreeJavaClassChooserDialog selector = new TreeJavaClassChooserDialog(
                "请选择要把方法添加到哪个Controller中", event.getProject(), servicePsiClass);
        selector.show();
        PsiClass controllerPsiClass = selector.getSelected();
        if (controllerPsiClass == null) {
            throw new RuntimeException("未选择控制器");
        }
        PsiAnnotation restControllerAnn =
                controllerPsiClass.getAnnotation("org.springframework.web.bind.annotation.RestController");
        if (restControllerAnn == null) {
            PsiAnnotation controllerAnn = controllerPsiClass.getAnnotation("org.springframework.stereotype.Controller");
            if (controllerAnn == null) {
                throw new RuntimeException("选择的类不是一个Controller类");
            }
        }

        //检查方法名称
        boolean b = checkMethodName(controllerPsiClass, handleMethod);
        if (!b) {
            return;
        }

        //是否还需要注入
        PsiField[] fields = controllerPsiClass.getFields();
        boolean needAddService = true;
        for (PsiField psiField : fields) {
            if (servicePsiClass.getQualifiedName()
                    .equals(psiField.getType().getInternalCanonicalText())) {
                needAddService = false;
                break;
            }
        }

        //添加注入
        if (needAddService) {
            importContent.add(servicePsiClass.getQualifiedName());
            importContent.add("org.springframework.beans.factory.annotation.Autowired");
            //如果需要添加注入，则表示原本控制器可能没有其它方法，则导入以下
            importContent.add("gynsh.scf.basics.define.vo.ResultVo");
            importContent.add("io.swagger.annotations.ApiOperation");
            importContent.add("org.springframework.web.bind.annotation.PostMapping");
            importContent.add("org.springframework.web.bind.annotation.RequestBody");
            importContent.add("javax.validation.Valid");

            WriteCommandAction.runWriteCommandAction(event.getProject(), () -> {
                String javaCode = FreemarkerUtils.processJavaCode("serviceAutowired.ftl",
                        new AutowiredParam(servicePsiClass.getName()));

                PsiElementFactory elementFactory = PsiElementFactory.getInstance(event.getProject());
                PsiField fieldFromText = elementFactory.createFieldFromText(javaCode, null);
                controllerPsiClass.add(fieldFromText);
            });
        }

        //获取控制器文件添加导入
        PsiJavaFile controllerJavaFile = (PsiJavaFile) controllerPsiClass.getContainingFile();
        PsiManager psiManager = PsiManager.getInstance(event.getProject());
        for (String s : importContent) {
            PsiClass psiClass = ClassUtil.findPsiClass(psiManager, s);
            WriteCommandAction.runWriteCommandAction(event.getProject(), () -> {
                controllerJavaFile.importClass(psiClass);
            });
        }

        WriteCommandAction.runWriteCommandAction(event.getProject(), () -> {
            //方法类容
            String javaCode = FreemarkerUtils.processJavaCode("serviceMethodTransform.ftl", handleMethod);

            PsiElementFactory elementFactory = PsiElementFactory.getInstance(event.getProject());
            PsiMethod createPsiMethod = elementFactory.createMethodFromText(javaCode, null);
            controllerPsiClass.add(createPsiMethod);
        });
    }

    private static boolean checkMethodName(PsiClass controllerPsiClass, HandleMethod handleMethod) {
        Set<String> methodNameSet = new HashSet<>();
        Set<String> mappingValueSet = new HashSet<>();
        for (PsiMethod method : controllerPsiClass.getMethods()) {
            methodNameSet.add(method.getName());
            PsiAnnotation annotation = method.getAnnotation("org.springframework.web.bind.annotation.PostMapping");
            if (annotation == null) {
                continue;
            }
            PsiAnnotationMemberValue value = annotation.findAttributeValue("value");
            if (value == null) {
                continue;
            }
            mappingValueSet.add(value.getText().replace("\"", ""));
        }

        String name = checkValue(methodNameSet, handleMethod.getName(), "方法名");
        if (CommonUtils.isBlank(name)) {
            return false;
        }
        handleMethod.setName(name);
        String mappingValue = checkValue(mappingValueSet, handleMethod.getMappingValue(), "Mapping值");
        if (CommonUtils.isBlank(mappingValue)) {
            return false;
        }
        handleMethod.setMappingValue(mappingValue);
        return true;
    }

    private static String checkValue(Set<String> set, String value, String message) {
        if (!set.contains(value)) {
            return value;
        }
        value = Messages.showInputDialog(
                message + "[" + value + "]已存在，请输入新的" + message,
                "Build Error",
                Messages.getErrorIcon(),
                value, null);
        return checkValue(set, value, message);
    }
}
